Imagine a system where there are several toll tag reporting stations, each of which need to be able to quickly record vehicles passing through a toll both quickly. They do not have time to wait for a message to some back-end system to complete before the next car passes through. This is a typical example where we want to use asynchronous messaging.
A client traditionally waits for a message/function to complete before it continues doing what it was doing. This is called synchronous messaging. In our case, we don't want to force the client to wait for the back-end processing to be complete before it can continue doing what it was doing. Instead, the client will create a message and send it to some back-end system. This allows the client to "fire and forget", sort of like sending an email message.
JEE's Answer
A solution can use Message Driven Beans (MBD) to address this kind of functionality. Originally MDB's were written on top of JMS but with the 2.1 and 3.0 specifications, Message Driven Beans can sit on top of many different kinds of resources. For our purposes, we'll simply stick to JMS.
A Message Driven Bean is a stateless enterprise bean. A container typically manages a pool of such objects. As messages arrive, the container will take an MDB from the pool and have it process a message. If the processing succeeds, the message is removed from the queue; otherwise it remains on the queue.
To demonstrate/simulate a client, we'll have a unit test that sends a message via JMS to a client.
For the remainder of this tutorial, when you see <project>, replace it with Ejb3Tutorial5.
Create the Project
Pull down the File menu and select new:project
Select Java Project
Click Next
Enter <project>
Under Project Layout select create separate source and output folders
Click Finish
Select <project>, right-click and select new:Source Folder
Enter conf for the name
Click on Finish
Select <project>, right-click and select new:Source Folder
Enter test for the name
Click on Finish
Edit your project properties
Now that we have created a user library, we can add that user library to our project:
Select <project>, and press alt-enter or right-click and select properties.
Select Java Build Path
Select the Libraries tab
Click on Add Library
Select User Library and click Next
Click on the box next to EJB3_EMBEDDABLE and click Finish
Click Add Library
Select JUnit and click Next
In the pull-down list, select JUnit 4 and click Finish
Click on OK to make the change to your project's classpath
Setup the configuration files
The JBoss Embeddable container looks for several files in the classpath. To keep all of these in one place, we'll add another source directory to our project and then import several files into that directory.
Select the conf folder under <project>
Pull down the File menu and select Import
Expand General
Select File System and click on Next
Click on Browse and go to the following directory: C:/libs/jboss-EJB-3.0_Embeddable_ALPHA_9/conf
Click on OK
You'll see conf in the left pane, select it
Verify that the Into folder: lists <project>/conf (if not, just enter it or browse to it)
Click Finish
Expand the conf directory and verify that the files are now there
Add Resource Adapter Archive(RAR)
The Java Connector system defines Resource Adapter Archive files (RAR files). We need to add a few RAR files into the class path. We will import two more files into the conf directory:
Select the conf folder
Pull down the File menu and select Import
Expand General
Select File System and click on Next
Click on Browse and go to the following directory: C:/libs/jboss-EJB-3.0_Embeddable_ALPHA_9/lib
Select jcainflow.rar and jms-ra.rar
Click Finish
Create a jndi.properties file
Note, depending on the version of the embeddable container you download, you might already have a file called jndi.properties. If you do, skip to the next section.
Select the conf directory, right-click and select new then select File
Enter the name jndi.properties and click finish
Enter the following 2 lines then save and close the file:
This example presents a utility class we'll be using later. The container needs a persistence.xml file to operate. This file must be found under a META-INF directory somewhere in the classpath or the embeddable container will not start. The file's name is persistence.xml with a lower-case 'p'. On a Unix system, this will make a difference. On a PC, this won't make a difference and it is one of those things that might work on your machine but not on the linux build box.
Select your src directory
Right-click, select New:Folder
Enter META-INF
Click OK
Select META-INF
Right-lick, select New:File
Enter persistence.xml
Click Finish
Copy the following example into your new file then save it by pressing ctrl-s
persistence.xml
<?xmlversion="1.0"encoding="UTF-8"?><persistence><persistence-unitname="custdb"><!-- This persistence unit uses the default data source that JBoss --><!-- defines called DefaultDS. If we wanted to use our own data --><!-- source, we'd need to define a custom data source somewhere. --><!-- That somewhere is vendor specific. --><!-- In the case of JBoss, since we're using the embedded container, --><!-- we need to add two beans in a file called --><!-- embedded-jboss-beans.xml. We name the first --><!-- HypersonicLocalServerDSBootstrap and we name the second --><!-- HypersonicLocalServerDS. This two step process defines a data --><!-- source. --><!-- In the first bean definition, we additionally bind it in Jndi --><!-- under some name. If we used the name --><!-- java:/HypersonicLocalServerDS then we would use the following --><!-- entry to use that data source instead of the default one: --><!-- <jta-data-source>java:/HypersonicLocalServerDS</jta-data-source> --><jta-data-source>java:/DefaultDS</jta-data-source><properties><propertyname="hibernate.hbm2ddl.auto"value="create-drop"/></properties></persistence-unit></persistence>
The Entity Model
We've taken the domain model from the previous EJB tutorial and added a "charge" object. An account has one to many charges associated with it. Otherwise, this domain model is unchanged from the previous tutorial.
This is the heart of our next tutorial, the Message Driven Bean:
packagemdb;importjavax.ejb.ActivationConfigProperty;importjavax.ejb.EJB;importjavax.ejb.MessageDriven;importjavax.jms.JMSException;importjavax.jms.MapMessage;importjavax.jms.Message;importjavax.jms.MessageListener;importsession.AccountInventory;/**
* The MessageDriven annotation declares this class to be a message driven bean.
* With EJB 3.0, you add a set of properties to the MDB via the
* ActivationConfigProperty annotation to indicate how to configure this MDB. In
* this case we set the destinationType to be a Queue (point to point) and the
* destination to queue/tolltag. These are the standard properties you'll need
* to set on JMS-based, point-to-point (queue)-based MDB's.
*/
@MessageDriven(activationConfig = {
@ActivationConfigProperty(propertyName = "destinationType",
propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(propertyName = "destination",
propertyValue = "queue/tolltag")})publicclass TollTagChargesMdb implements MessageListener {/**
* I can refer to other Session Beans as demonstrated here.
*/
@EJB
private AccountInventory bean;/**
* When a message comes in, my onMessage is executed by the container. In
* this case, I expect a MapMessage (this of this as a hash table). The
* implementation is simple, grab the information out of the map and send a
* message to a Stateless Session Bean (AccountInventoryBean) to add a
* charge.
*/publicvoid onMessage(final Message message){final MapMessage mm = (MapMessage) message;try{finalString tollTagNumber = mm.getString("tollTagNumber");finaldouble amount = mm.getDouble("amount");
bean.addCharge(tollTagNumber, amount);}catch(JMSException e){thrownewRuntimeException("Problem getting properties out of message");}}}
This bean is created by the container as necessary (probably pooled at container start time). When the container sees a message, it starts a transaction (if necessary) and then waits for the onMessage to complete.
What do you think about whether objects are managed or unmanaged while being processed by the MDB?
Who creates the transactions for these updates?
The test uses Thread.sleep(1000). Why does it do so? Experiment with that value, is 1000 milliseconds the “right” value? Suggest a better way to test this. (Consider Aspect Oriented Programming or interceptors or even static variables.)
Full Sources
Here is the source for all of the classes not already provided in full above.
Account.java
packageentity;importjava.util.ArrayList;importjava.util.Collection;importjavax.persistence.CascadeType;importjavax.persistence.Entity;importjavax.persistence.GeneratedValue;importjavax.persistence.Id;importjavax.persistence.OneToMany;/**
* This is a simple Account class that knows about toll tags and vehicles. An
* account has a one to many relationship with both toll tags and vehicles. By
* default, one to many relationships are lazily loaded. To demonstrate
* differences between extended scope contexts and transaction-scoped contexts,
* one of these relationships is eagerly fetched.
*/
@Entitypublicclass Account {
@Id
@GeneratedValue
privateLong id;/**
* This relationship is lazily fetched. This means a client using a detached
* Account will not be able to access tollTags unless the relationship was
* touched while the object was still managed.
*/
@OneToMany(mappedBy = "account", cascade = CascadeType.ALL)privateCollection<TollTag> tollTags;/**
* We eagerly fetch this relationship to show that doing so allows this
* relationship to work if the object is or is not detached.
*/
@OneToMany(mappedBy = "account", cascade = CascadeType.ALL)privateCollection<Vehicle> vehicles;
@OneToMany(mappedBy = "account", cascade = CascadeType.ALL)privateCollection<Charge> charges;public Account(){}publicLong getId(){return id;}publicCollection<TollTag> getTollTags(){if(tollTags == null){
tollTags = newArrayList<TollTag>();}return tollTags;}publicCollection<Vehicle> getVehicles(){if(vehicles == null){
vehicles = newArrayList<Vehicle>();}return vehicles;}publicCollection<Charge> getCharges(){if(charges == null){
charges = newArrayList<Charge>();}return charges;}publicvoid addVehicle(final Vehicle vehicle){
getVehicles().add(vehicle);
vehicle.setAccount(this);}publicvoid removeVehicle(final Vehicle vehicle){
getVehicles().remove(vehicle);
vehicle.setAccount(null);}publicvoid addTollTag(final TollTag tollTag){
getTollTags().add(tollTag);
tollTag.setAccount(this);}publicvoid addCharge(final Charge charge){
getCharges().add(charge);
charge.setAccount(this);}publicvoid removeTollTag(final TollTag tt){
getTollTags().remove(tt);
tt.setAccount(null);}}
packageutil;/**
* We typically need to compare two object and also perform null checking. This
* class provides a simple wrapper to accomplish doing so.
*/publicclass EqualsUtil {private EqualsUtil(){// I'm a utility class, do not instantiate me}publicstaticboolean equals(finalObject lhs, finalObject rhs){return lhs == null&& rhs == null
|| (lhs != null&& rhs != null&& lhs.equals(rhs));}}
JBossUtil.java
packageutil;importjava.io.ByteArrayOutputStream;importjava.io.IOException;importjava.io.OutputStream;importjava.io.PrintStream;importjava.util.logging.Logger;importjavax.naming.InitialContext;importjavax.naming.NamingException;importorg.jboss.ejb3.embedded.EJB3StandaloneBootstrap;/**
* This class was originally necessary when using the ALPHA 5 version of the
* embeddable container. With the alpha 9 release, initialization is quite
* simple, you need just 2 lines to initialize your JBoss Embeddable EJB3
* Container Environment. Unfortunately, the one that is available for download
* uses System.out.println() in a few places, so this simple utility hides that
* output and also provides a simple lookup mechanism.
*/publicclass JBossUtil {privatestaticPrintStream originalOut;privatestaticPrintStream originalErr;privatestaticOutputStream testOutputStream;privatestaticPrintStream testOuputPrintStream;staticboolean initialized;staticInitialContext ctx;private JBossUtil(){// I'm a utility class, do not instantiate me}/**
* JBoss EJB3 Embeddable Container uses System.out. Redirect that output to
* keep the console output clean.
*/privatestaticvoid redirectStreams(){// configure the console to get rid of hard-coded System.out's in// the JBoss libraries
testOutputStream = newByteArrayOutputStream(2048);
testOuputPrintStream = newPrintStream(testOutputStream);
originalOut = System.out;
originalErr = System.err;System.setOut(testOuputPrintStream);System.setErr(testOuputPrintStream);}/**
* Restore the System.out and System.err streams to their original state.
* Close the temporary stream created for the purpose of redirecting I/O
* while the initializing is going on.
*/privatestaticvoid restoreStreams(){System.setOut(originalOut);System.setErr(originalErr);
testOuputPrintStream.close();try{
testOutputStream.close();}catch(IOException e){Logger.getLogger(JBossUtil.class.getName()).info("Unable to close testoutstream");}}/**
* This method starts and initializes the embeddable container. We do not
* offer a method to properly clean up the container since this is really
* meant for testing only.
*
* This method may freely be called as often as you'd like since it lazily
* initializes the container only once.
*/publicstaticvoid startDeployer(){if(!initialized){
redirectStreams();
EJB3StandaloneBootstrap.boot(null);
EJB3StandaloneBootstrap.deployXmlResource("jboss-jms-beans.xml");
EJB3StandaloneBootstrap.deployXmlResource("tolltag-jms.xml");
EJB3StandaloneBootstrap.scanClasspath();
initialized = true;
restoreStreams();}}/**
* This is for symmetry. Given how we are using this class, there's little
* need to actually shutdown the container since we run a quick application
* and then stop the JVM.
*/publicstaticvoid shutdownDeployer(){
EJB3StandaloneBootstrap.shutdown();}privatestaticInitialContext getContext(){/**
* We only keep one context around, so lazily initialize it
*/if(ctx == null){try{
ctx = newInitialContext();}catch(NamingException e){thrownewRuntimeException("Unable to get initial context", e);}}return ctx;}/**
* The lookup method on InitialContext returns Object. This simple wrapper
* asks first for the expected type and the for the name to find. It gets
* the name out of JNDI and performs a simple type-check. It then casts to
* the type provided as the first parameter.
*
* This isn't strictly correct since the cast uses the expression (T), where
* T is the generic parameter and the type is erased at run-time. However,
* since we first perform a type check, we know this cast is safe. The -at-
* SuppressWarnings lets the Java Compiler know that we think we know what
* we are doing.
*
* @param <T>
* Type type provided as the first parameter
* @param clazz
* The type to cast to upon return
* @param name
* The name to find in Jndi, e.g. XxxDao/local or, XxxDao/Remote
* @return Something out of Jndi cast to the type provided as the first
* parameter.
*/
@SuppressWarnings("unchecked")publicstatic<T> T lookup(Class<T> clazz, String name){finalInitialContext ctx = getContext();/**
* Perform the lookup, verify that it is type-compatible with clazz and
* cast the return type (using the erased type because that's all we
* have) so the client does not need to perform the cast.
*/try{finalObject object = ctx.lookup(name);if(clazz.isAssignableFrom(object.getClass())){return(T) object;}else{thrownewRuntimeException(String.format("Class found: %s cannot be assigned to type: %s",
object.getClass(), clazz));}}catch(NamingException e){thrownewRuntimeException(String.format("Unable to find ejb for %s", clazz.getName()), e);}}}
Table of Contents
Ejb3 Tutorial 5 - Message Drive Beans
This tutorial is a derivative of one of the JBoss tutorials written using the embeddable container. In this tutorial we take the domain from EJB3 Tutorial 4 - Extended Context and add a new idea.The Requirements
Imagine a system where there are several toll tag reporting stations, each of which need to be able to quickly record vehicles passing through a toll both quickly. They do not have time to wait for a message to some back-end system to complete before the next car passes through. This is a typical example where we want to use asynchronous messaging.A client traditionally waits for a message/function to complete before it continues doing what it was doing. This is called synchronous messaging. In our case, we don't want to force the client to wait for the back-end processing to be complete before it can continue doing what it was doing. Instead, the client will create a message and send it to some back-end system. This allows the client to "fire and forget", sort of like sending an email message.
JEE's Answer
A solution can use Message Driven Beans (MBD) to address this kind of functionality. Originally MDB's were written on top of JMS but with the 2.1 and 3.0 specifications, Message Driven Beans can sit on top of many different kinds of resources. For our purposes, we'll simply stick to JMS.A Message Driven Bean is a stateless enterprise bean. A container typically manages a pool of such objects. As messages arrive, the container will take an MDB from the pool and have it process a message. If the processing succeeds, the message is removed from the queue; otherwise it remains on the queue.
To demonstrate/simulate a client, we'll have a unit test that sends a message via JMS to a client.
Project Setup
The instructions for setting up your project mirror those from the first tutorial: EJB3 Tutorial 1 - Create and Configure.For the remainder of this tutorial, when you see <project>, replace it with Ejb3Tutorial5.
Create the Project
Edit your project properties
Now that we have created a user library, we can add that user library to our project:Setup the configuration files
The JBoss Embeddable container looks for several files in the classpath. To keep all of these in one place, we'll add another source directory to our project and then import several files into that directory.Add Resource Adapter Archive(RAR)
The Java Connector system defines Resource Adapter Archive files (RAR files). We need to add a few RAR files into the class path. We will import two more files into the conf directory:Create a jndi.properties file
Note, depending on the version of the embeddable container you download, you might already have a file called jndi.properties. If you do, skip to the next section.Create a persistence.xml
This example presents a utility class we'll be using later. The container needs a persistence.xml file to operate. This file must be found under a META-INF directory somewhere in the classpath or the embeddable container will not start. The file's name is persistence.xml with a lower-case 'p'. On a Unix system, this will make a difference. On a PC, this won't make a difference and it is one of those things that might work on your machine but not on the linux build box.persistence.xml
The Entity Model
We've taken the domain model from the previous EJB tutorial and added a "charge" object. An account has one to many charges associated with it. Otherwise, this domain model is unchanged from the previous tutorial.Charge.java
To update the Account object, add a new relationship called "charges" as follows:
The Session Model
We need to update the AccountInventory interface and the beans. Make the following changes:public void addCharge(String tollTagNumber, double amount) { final Account account = findAccountByTagNumber(tollTagNumber); final Charge charge = new Charge(account, amount, Calendar .getInstance().getTime()); account.addCharge(charge); } @SuppressWarnings("unchecked") public double getTotalChargesOnAccountById(Long id) { final List<Charge> charges = getEm().createNamedQuery( "Charge.forAccount").setParameter("accountId", id) .getResultList(); double total = 0; for (Charge c : charges) { total += c.getAmount(); } return total; }Changes to util
The Message Driven Bean
This is the heart of our next tutorial, the Message Driven Bean:This bean is created by the container as necessary (probably pooled at container start time). When the container sees a message, it starts a transaction (if necessary) and then waits for the onMessage to complete.
The Test
Assignments/Questions
What do you think about whether objects are managed or unmanaged while being processed by the MDB?Who creates the transactions for these updates?
The test uses Thread.sleep(1000). Why does it do so? Experiment with that value, is 1000 milliseconds the “right” value? Suggest a better way to test this. (Consider Aspect Oriented Programming or interceptors or even static variables.)
Full Sources
Here is the source for all of the classes not already provided in full above.Account.java
Charge.java
TollTag.java
Vehicle.java
AccountInventory.java
AccountInventoryBean.java
EqualsUtil.java
JBossUtil.java
persistence.xml
AccountInventoryBeanTest.java
tolltag-jms.xml
.classpath
<--Back